findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
Relationship with Fourier Transform
If the ROC includes the unit circle, \(|z| = 1\), then:
Zeros: Roots of the numerator \(\Rightarrow z = 0.5\)
Poles: Roots of the denominator \(\Rightarrow z = 0.9\)
Pole-Zero Plot
Visualizes system behavior
Check for: - Stability (poles inside unit circle) - Frequency shaping
Practice
Convert this equation:
\[y[n] = 0.6 y[n-1] + x[n] + x[n-1]\]
Find: - \(H(z)\) - Poles and zeros - Plot them in the Z-plane
Application in Biosignal Processing
Analysis of digital filters for ECG, EEG, etc.
Design of stable and causal filtering systems
Useful in difference equation modeling of biosignals
Summary
Z-Transform is a powerful tool for analyzing discrete systems
Provides insight into stability, causality, and system behavior
A generalization of the Fourier Transform
Crucial in digital signal processing of biosignals
Z-Transform converts difference equations into algebraic expressions
Transfer function \(H(z)\) tells us how the system responds to inputs
Key for digital filter design in biosignal processing
Filter Design Methods
Next Steps
Practice Z-Transform computations
Pole-zero plotting exercises
Application to real biosignal filtering problems
Filter Design
Notch Filter
A common issue in biosignal processing is removing power‐line interference (50/60 Hz) from, for example, EEG or ECG signals. A simple digital filter to eliminate 60 Hz interference (assuming a sampling frequency \(f_s = 5000\) Hz) is to place complex‐conjugate zeros at
This \(H(z)\) has zeros at \(e^{\pm j2\pi(60/5000)}\) that precisely cancel the 60 Hz component, thereby implementing a notch filter. Moreover, it is a second‐order FIR filter with symmetric coefficients, which grants it linear phase (important to avoid waveform distortion).
Filter Design
def design_filter(zeros=None, poles=None, gain=1.0):""" Diseña un filtro digital a partir de ceros y/o polos y una ganancia. Parámetros: - zeros: lista de ceros (raíces del numerador), o None para no incluir - poles: lista de polos (raíces del denominador), o None para no incluir - gain: ganancia escalar del filtro Devuelve: - b: coeficientes del numerador - a: coeficientes del denominador """# Si no se pasan ceros, asumimos un FIR trivial (b = [gain])if zeros: b = gain * np.poly(zeros)else: b = np.array([gain], dtype=float)# Si no se pasan polos, asumimos sistema FIR (a = [1])if poles: a = np.poly(poles)else: a = np.array([1.0], dtype=float)return b, a
Filter Design
def gaussian(x, mu, sigma, A):""" Genera una función gaussiana. Parámetros: - x: array de tiempos - mu: posición central de la gaussiana - sigma: desviación estándar - A: amplitud """return A * np.exp(-((x - mu) **2) / (2* sigma**2))
Filter Design
def simulate_ecg(duration=10.0, fs=500, heart_rate=60):""" Simula un ECG sintético basado en la superposición de ondas gaussianas. Parámetros: - duration: duración de la señal en segundos - fs: frecuencia de muestreo en Hz - heart_rate: frecuencia cardiaca en latidos por minuto Devuelve: - t: vector de tiempos - ecg: señal simulada de ECG en mV """ dt =1/ fs t = np.arange(0, duration, dt) rr =60/ heart_rate # intervalo RR en segundos# Inicializar señal ecg = np.zeros_like(t)# Parámetros de las ondas (posiciones relativas en segundos)# P wave p_amp, p_dur, p_delay =0.25, 0.09, 0.16# Q wave q_amp, q_dur, q_delay =-0.05, 0.066, 0.166# R wave r_amp, r_dur, r_delay =1.6, 0.1, 0.166# S wave s_amp, s_dur, s_delay =-0.25, 0.066, 0.19# T wave t_amp, t_dur, t_delay =0.35, 0.142, 0.36# Generar cada latidofor beat_start in np.arange(0, duration, rr): mask = (t >= beat_start) & (t < beat_start + rr) tb = t[mask] - beat_start ecg[mask] += gaussian(tb, p_delay, p_dur /2, p_amp) ecg[mask] += gaussian(tb, q_delay, q_dur /2, q_amp) ecg[mask] += gaussian(tb, r_delay, r_dur /2, r_amp) ecg[mask] += gaussian(tb, s_delay, s_dur /2, s_amp) ecg[mask] += gaussian(tb, t_delay, t_dur /2, t_amp)return t, ecg+0.1*np.cos(2*np.pi*60*t)
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
plt.show()
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
Filter Design
n =len(ecg_signal)yf = np.fft.fft(ecg_signal)xf = np.fft.fftfreq(n, 1/fs)[:n//2]plt.plot(xf, 2.0/n * np.abs(yf[0:n//2]))plt.grid()plt.show()
Filter Design
fc =60b,a =design_filter(zeros=[np.exp(1j*2*np.pi*fc/fs), np.exp(-1j*2*np.pi*fc/fs)])print(a)print(b)w, h = sig.freqz(a, b, worN=8000)plt.plot(w/np.pi*fs/2, 20*np.log10(abs(h)))plt.grid()plt.xlabel('Frequency (Hz)')
FIR Filter Design Method
Filter Design
FIR filters
FIR filters (Finite Impulse Response) are widely used in biomedical processing because they can be designed to have linear phase response, avoiding phase distortion in the filtered signal (which is useful for preserving the morphology of ECG waves, for example).
Filter Design
Filter Design Process
Defining the desired ideal frequency response \(H_d(e^{j\omega})\).
Obtaining the ideal impulse response \(h_d[n]\) as the inverse Fourier transform of \(H_d\).
Truncating \(h_d[n]\) (which is usually infinite or very long) with a window function \(w[n]\) to obtain a realizable FIR filter
\[
h[n] = h_d[n]\,w[n].
\]
Filter Design
You obtain \(h_d[n]\) as the inverse discrete–time Fourier transform of your ideal frequency response \(H_d(e^{j\omega})\). For a low-pass filter with cutoff \(\omega_c\),
where we define the normalized sinc as \(\mathrm{sinc}(x)=\frac{\sin(\pi x)}{\pi x}\).
Key takeaway
\(h_d[n]\) is exactly the inverse‐DTFT of the ideal (“brick‐wall”) frequency specification.
It produces a sinc-shaped impulse response of infinite length.
Truncation (with a window) makes it realizable as a finite-length FIR.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family ['Fira Code'] not found. Falling back to DejaVu Sans.
findfont: Font family ['Fira Code'] not found. Falling back to DejaVu Sans.
findfont: Font family ['Fira Code'] not found. Falling back to DejaVu Sans.
findfont: Font family ['Fira Code'] not found. Falling back to DejaVu Sans.
findfont: Font family 'Fira Code' not found.
findfont: Font family ['Fira Code'] not found. Falling back to DejaVu Sans.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
Filter Design
The typical characteristics of classic windows are:
Rectangular: narrowest main lobe (width ≈ 4π/M rad) but highest sidelobes (first sidelobe ≈ –13 dB, stop-band attenuation ≈ 21 dB). It gives the steepest transition for a given filter order, at the expense of poorer stop-band rejection.
Hann: main lobe ≈ 8π/M, sidelobes ≈ –31 dB (better rejection than rectangular but smoother transitions).
Hamming: main lobe ≈ 8π/M, sidelobes ≈ –41 dB (minimum stop-band attenuation ≈ 53 dB). Very popular for its good compromise between transition width and stop-band attenuation.
Blackman: wider main lobe (≈ 12π/M) but very low sidelobes (≈ –57 dB, attenuation ≈ 74 dB).
Kaiser: allows selection of a parameter β to control sidelobe attenuation, offering flexibility. Approximately, to achieve A dB of attenuation,
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
(-100.0, 5.0)
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
import numpy as npimport matplotlib.pyplot as pltfrom scipy.signal.windows import ( boxcar, # Rectangular bartlett, # Triangular hann, # Hann hamming, # Hamming blackman, # Blackman kaiser # Kaiser)# ParametersM =64# window lengthbeta =8.6# Kaiser parameter for moderate sidelobe attenuationnfft =512# FFT length for frequency responsefs =1.0# normalized sampling rate# Generate windowswindows = {'Rectangular': boxcar(M),'Bartlett': bartlett(M),'Hann': hann(M),'Hamming': hamming(M),'Blackman': blackman(M),f'Kaiser (β={beta})': kaiser(M, beta)}# Time-domain plotplt.figure(figsize=(8, 4))for name, w in windows.items(): plt.plot(np.arange(M), w, label=name)plt.title('Window Functions — Time Domain')plt.xlabel('Sample index n')plt.ylabel('Amplitude')plt.legend(loc='upper right')plt.grid(True)plt.tight_layout()# Frequency-domain plotplt.figure(figsize=(8, 4))for name, w in windows.items():# Compute normalized frequency response W = np.fft.fft(w, nfft) W_mag =20* np.log10(np.abs(W) / np.max(np.abs(W))) freqs = np.fft.fftfreq(nfft, d=1/fs)# Only plot 0 ≤ f ≤ 0.5 (normalized Nyquist) idx = np.logical_and(freqs >=0, freqs <=0.5) plt.plot(freqs[idx], W_mag[idx], label=name)plt.title('Window Functions — Frequency Response')plt.xlabel('Normalized Frequency (cycles/sample)')plt.ylabel('Magnitude (dB)')plt.ylim(-100, 5)plt.legend(loc='upper right')plt.grid(True)plt.tight_layout()plt.show()
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
findfont: Font family 'Fira Code' not found.
N =101fc =30# 2. Compute the ideal impulse response h_d[n] of a low-pass filtern = np.arange(N)M = (N -1) /2# Using the normalized sinc: sinc(x) = sin(pi*x)/(pi*x)h_d = (2* fc / FS) * np.sinc(2* fc * (n - M) / FS)# 3. Choose a window w[n] (here: Hamming) and truncatew = np.hamming(N)h = h_d * w# 4. Normalize to ensure unity gain at DCh /= np.sum(h)# 6. Filter it (only allowed library call)y = sig.lfilter(h, 1.0, ecg_signal)# 7. Plot input vs. outputplt.figure(figsize=(8, 4))plt.plot(t, ecg_signal, label='Original signal')plt.plot(t, y, label='Filtered signal', linewidth=2)plt.xlabel('Time [s]')plt.ylabel('Amplitude')plt.title('Low-pass FIR (window method) – Cutoff 100 Hz')plt.legend()plt.grid(True)plt.tight_layout()plt.show()
IIR Filter Design Method
Introduction: IIR Filter Design Strategy
Goal: Design digital Infinite Impulse Response (IIR) filters.
Approach: Leverage well-established theory and techniques from analog filter design.
Method:
Design a suitable analog filter prototype \(H(s)\).
Transform this analog design into a digital filter \(H(z)\).
Step 0: The Analog Domain - Laplace Transform
Context: Continuous-time signals and Linear Time-Invariant (LTI) systems.
Tool: The Laplace Transform converts differential equations (time domain) to algebraic equations (s-domain).
Transfer Function \(H(s)\): Represents the system in the s-domain. \[H(s) = \frac{Y(s)}{X(s)}\] where \(s = \sigma + j\Omega\) is the complex frequency (\(\Omega\) = analog angular frequency).
Step 0: The Analog Domain - s-Plane Analysis
s-Plane: Complex plane where \(H(s)\) is analyzed.
Poles & Zeros: Roots of the denominator & numerator of \(H(s)\), respectively. Their locations determine filter behavior.
Frequency Response: Determined by \(H(j\Omega)\) (evaluating \(H(s)\) on the imaginary axis).
Stability: For a causal system to be stable, all poles must be in the Left-Half Plane (LHP), i.e., \(\text{Re}\{s\} < 0\).
Step 0: The Analog Domain - Filter Prototypes
Standardized, well-understood analog filter approximations:
Butterworth: Maximally flat passband.
Chebyshev Type I: Equiripple passband, monotonic stopband.
Chebyshev Type II: Monotonic passband, equiripple stopband.
Elliptic (Cauer): Equiripple in both passband and stopband (sharpest transition for a given order).
Design usually starts with a normalized low-pass prototype.
The Bridge: Analog-to-Digital Mapping
Core Task: Convert the analog filter \(H(s)\) (s-plane) into a digital filter \(H(z)\) (z-plane).
Need a mapping that relates the continuous frequency \(\Omega\) to the discrete frequency \(\omega\).
Crucially, the stable region (LHP in s-plane) should map to the stable region (inside the unit circle in z-plane).
Step 1: Digital Filter Specifications
Define the desired characteristics of the digital filter:
Critical Frequencies:
Passband edge: \(\omega_p\)
Stopband edge: \(\omega_s\)
(Normalized digital angular frequencies, e.g., \(0 \le \omega \le \pi\))
Tolerances:
Max. passband ripple/attenuation: \(A_p\) (dB) or \(\delta_p\)
Min. stopband attenuation: \(A_s\) (dB) or \(\delta_s\)
Step 2: Transformation Methods - Overview
Mathematical mappings from the s-plane to the z-plane.
Two primary methods:
Impulse Invariance
Bilinear Transformation
Step 2: Method 1 - Impulse Invariance
Concept: Match the impulse response: \(h_{digital}[n] \approx T \cdot h_{analog}(nT)\).
Mapping: Maps s-plane pole \(s_k\) to z-plane pole \(z_k = e^{s_k T}\).
Uses partial fraction expansion of \(H(s)\).
Pros: Preserves impulse response shape.
Cons: Suffers from frequency aliasing if \(H(j\Omega)\) is not bandlimited below Nyquist frequency (\(F_s/2\)). Best for low-pass/band-pass filters.
Step 2: Method 2 - Bilinear Transformation
Concept: An algebraic substitution mapping the \(j\Omega\) axis onto the unit circle.
Mapping Formula:\[s = \frac{2}{T} \frac{1 - z^{-1}}{1 + z^{-1}}\] (T = sampling period, often set to 2 for simplicity during derivation).
Pros:
No aliasing: Maps entire \(j\Omega\) axis to the unit circle (\(-\pi \le \omega \le \pi\)).
Preserves stability: Maps LHP (stable s-region) to inside the unit circle (stable z-region).
Cons: Introduces non-linear frequency warping.
Understanding Frequency Warping (Bilinear)
The relationship between analog (\(\Omega\)) and digital (\(\omega\)) frequencies is non-linear: \[\Omega = \frac{2}{T} \tan\left(\frac{\omega}{2}\right)\] or equivalently: \[\omega = 2 \arctan\left(\frac{\Omega T}{2}\right)\]
This compresses the infinite analog frequency axis onto the finite digital frequency range \([-\pi, \pi]\).
The mapping is non-uniform, most distorted near \(\omega = \pm \pi\).
Step 3: Frequency Pre-warping (Bilinear Only)
Problem: Frequency warping distorts the locations of \(\omega_p\) and \(\omega_s\).
Solution: Pre-warp the digital specifications (\(\omega_p, \omega_s\)) into corresponding analog frequencies (\(\Omega_p, \Omega_s\)) before designing the analog filter.
Formula: Use the warping relationship: \[\Omega_p = \frac{2}{T} \tan\left(\frac{\omega_p}{2}\right)\]\[\Omega_s = \frac{2}{T} \tan\left(\frac{\omega_s}{2}\right)\]
Use these \(\Omega_p, \Omega_s\) values for the analog design step.
Step 4: Analog Prototype Design Workflow
Select Prototype: Choose Butterworth, Chebyshev, Elliptic based on specs (ripple, transition width) using the (pre-warped) \(\Omega_p, \Omega_s, A_p, A_s\).
Determine Order (N) & Cutoff (\(\Omega_c\)): Calculate required analog filter order and cutoff frequency using prototype-specific formulas.
Find Normalized LP \(H_{LP}(s)\): Obtain the transfer function for the normalized low-pass prototype.
Frequency Transformation (Analog): If needed, transform \(H_{LP}(s)\) to target type (HP, BP, BS) using analog transformations.
Result: The final analog filter transfer function \(H_a(s)\).
Analog Filter Design: Overview
Designing an analog filter translates desired signal filtering characteristics into a physical electronic circuit.
Key Stages:
Define Specifications
Choose an Approximation Method
Determine Filter Order
Find Normalized Low-Pass Prototype (Conceptual)
Denormalize & Transform (Conceptual)
Synthesize the Circuit
Component Selection & Simulation
We’ll illustrate steps 1, 3, and aspects of 4/5 using Python’s scipy.signal.
Critical Frequencies (Analog, \(\Omega\) in rad/s or \(f\) in Hz):
Cutoff (\(\Omega_c, f_c\))
Passband Edge(s) (\(\Omega_p, f_p\))
Stopband Edge(s) (\(\Omega_s, f_s\))
Attenuation Levels (dB):
Max. Passband Attenuation/Ripple (\(A_{max}\) or gpass)
Min. Stopband Attenuation (\(A_{min}\) or gstop)
Phase Response: Linear phase (e.g., Bessel) or not critical.
Impedance: Source/Load matching.
1. Specifications: Python Example (Low-Pass)
Let’s define specs for an analog low-pass filter. Frequencies for analog filters in scipy are typically in rad/s.
import numpy as npfrom scipy import signalimport matplotlib.pyplot as plt# Specifications for an ANALOG Low-Pass Filterfp_hz =1000# Passband edge frequency in Hzfs_hz =1500# Stopband edge frequency in Hzgpass_db =1.0# Max loss in passband (dB)gstop_db =40.0# Min attenuation in stopband (dB)# Convert Hz to rad/s for Scipy analog functionsWp_rads =2* np.pi * fp_hz # Passband edge in rad/sWs_rads =2* np.pi * fs_hz # Stopband edge in rad/sprint(f"Passband edge: {Wp_rads:.2f} rad/s ({fp_hz} Hz)")print(f"Stopband edge: {Ws_rads:.2f} rad/s ({fs_hz} Hz)")print(f"Max Passband Loss: {gpass_db} dB")print(f"Min Stopband Attenuation: {gstop_db} dB")
2. Choose an Approximation Method (Filter Family)
Select based on trade-offs (roll-off, ripple, phase).
Chebyshev Type I: Steeper roll-off than Butterworth, passband ripple.
Chebyshev Type II: Steeper roll-off, stopband ripple, flat passband.
Elliptic (Cauer): Steepest roll-off, ripple in both bands.
Bessel-Thomson: Best phase linearity (constant group delay), slowest roll-off.
For our Python examples, we’ll use Butterworth.
Butterworth Filter Fundamentals
The Butterworth filter is maximally flat in the passband.
The normalized prototype has a cutoff frequency \(\omega_c = 1\) rad/s.
Poles are placed on the unit circle in the \(s\)-plane at angles: \[s_k = e^{j\pi\frac{2k + n - 1}{2n}}, \quad k = 1,2,\ldots,n.\]
The Butterworth polynomial of order \(n\) is: \[B_n(s) = \prod_{k=1}^{n} (s - s_k).\]
Normalized Lowpass (LP) Transfer Function
The normalized \(n\)th-order lowpass prototype is: \[H_{LP}(s) = \frac{1}{B_n(s)} = \frac{1}{\prod_{k=1}^n (s - s_k)}.\] This yields a cutoff at \(|H_{LP}(j1)| = 1/\sqrt{2}\) (the –3 dB point).
Normalized Highpass (HP) Transfer Function
Apply the frequency transformation \(s \to 1/s\) and multiply by \(s^n\): \[H_{HP}(s) = H_{LP}(1/s)\,s^n = \frac{s^n}{B_n(1/s)} = \frac{s^n}{\prod_{k=1}^n (1/s - s_k)} = \frac{\prod_{k=1}^n(-s_k)\;s^n}{\prod_{k=1}^n(s - s_k^{-1})}.\] This prototype has a normalized cutoff at \(\omega=1\) rad/s in the highpass sense.
Normalized Bandpass (BP) Transfer Function
Use the bandpass transformation: \[s \to \frac{s^2 + \Omega_0^2}{B\,s},\] where \(\Omega_0 = \sqrt{\omega_1\omega_2}\) and \(B = \omega_2 - \omega_1\).
For a normalized prototype, we set \(\Omega_0 = 1\), \(B = 1\), giving: \[H_{BP}(s) = H_{LP}\left(\frac{s^2 + 1}{s}\right) = \frac{(s)^n}{\prod_{k=1}^n\left(\frac{s^2 + 1}{s} - s_k\right)} = \frac{s^n}{\prod_{k=1}^n\left(s^2 + 1 - s\,s_k\right)}.\]
Estimating the filter order (\(N\)) is a crucial first step in analog filter design after defining specifications. The order determines the filter’s complexity and steepness of its frequency response roll-off.
These equations are for Butterworth filters. The calculated \(N\) is a real number and must be rounded up to the next integer.
Common Variables: * \(N\): Filter order (integer, result of formula is rounded up). * \(A_{min}\): Minimum stopband attenuation (dB, positive value, e.g., 40 dB). * \(A_{max}\): Maximum passband attenuation/ripple (dB, positive value, e.g., 1 dB). * \(\log_{10}\): Base-10 logarithm. * All \(\Omega\) values are angular frequencies in rad/s.
Explanation of LP-Specific Variables: * \(\Omega_p\): Passband edge angular frequency. The frequency up to which signals pass with at most \(A_{max}\) attenuation. * \(\Omega_s\): Stopband edge angular frequency. The frequency from which signals are attenuated by at least \(A_{min}\). * For a low-pass filter, \(\Omega_s > \Omega_p\). The term \(\frac{\Omega_s}{\Omega_p}\) is the transition ratio.
Explanation of HP-Specific Variables: * \(\Omega_p\): Passband edge angular frequency. The frequency from which signals pass with at most \(A_{max}\) attenuation. * \(\Omega_s\): Stopband edge angular frequency. The frequency down to which signals are attenuated by at least \(A_{min}\). * For a high-pass filter, \(\Omega_p > \Omega_s\). The term \(\frac{\Omega_p}{\Omega_s}\) is the transition ratio (inverted compared to LP to keep it >1 for the logarithm).
Butterworth Band-Pass (BP) Filter Order
The order \(N_{BP}\) is the same as an equivalent Low-Pass Prototype. \[N_{BP} \ge \frac{\log_{10}\left(\frac{10^{A_{min}/10}-1}{10^{A_{max}/10}-1}\right)}{2 \log_{10}\left(\Omega_{s,LP_equiv}\right)}\]
Explanation of BP-Specific Variables: * \(\Omega_{p1}, \Omega_{p2}\): Passband edge angular frequencies (\(\Omega_{p1} < \Omega_{p2}\)). * \(\Omega_{s1}, \Omega_{s2}\): Stopband edge angular frequencies (\(\Omega_{s1} < \Omega_{p1}\) and \(\Omega_{s2} > \Omega_{p2}\)). * \(\Omega_0 = \sqrt{\Omega_{p1}\Omega_{p2}}\): Geometric center frequency of the passband. * \(B = \Omega_{p2} - \Omega_{p1}\): Bandwidth of the passband. * \(\Omega_{s,LP_equiv} = \min\left( \left| \frac{\Omega_{s1}^2 - \Omega_0^2}{\Omega_{s1} B} \right|, \left| \frac{\Omega_{s2}^2 - \Omega_0^2}{\Omega_{s2} B} \right| \right)\) * This \(\Omega_{s,LP_equiv}\) is the stopband edge of an equivalent low-pass prototype whose passband edge is normalized to 1 rad/s. It represents the more stringent of the two possible transitions from the passband edges to the stopband edges.
Butterworth Band-Stop (BS) Filter Order
The order \(N_{BS}\) is the same as an equivalent Low-Pass Prototype. \[N_{BS} \ge \frac{\log_{10}\left(\frac{10^{A_{min}/10}-1}{10^{A_{max}/10}-1}\right)}{2 \log_{10}\left(\Omega_{trans,LP_equiv}\right)}\]
Explanation of BS-Specific Variables: * \(\Omega_{s1}, \Omega_{s2}\): Stopband edge angular frequencies defining the rejected band (\(\Omega_{s1} < \Omega_{s2}\)). \(A_{min}\) is the attenuation in this band. * \(\Omega_{p1}, \Omega_{p2}\): Passband edge angular frequencies outside the stopband (\(\Omega_{p1} < \Omega_{s1}\) and \(\Omega_{p2} > \Omega_{s2}\)). \(A_{max}\) is the ripple in these passbands. * \(\Omega_0 = \sqrt{\Omega_{s1}\Omega_{s2}}\): Geometric center frequency of the stopband. * \(B = \Omega_{s2} - \Omega_{s1}\): Bandwidth of the stopband. * \(\Omega_{p,LP_i} = \left| \frac{\Omega_{pi} B}{\Omega_{pi}^2 - \Omega_0^2} \right|\) for \(i=1\) (using \(\Omega_{p1}\)) and \(i=2\) (using \(\Omega_{p2}\)). * These are the passband edges of an equivalent low-pass prototype whose stopband edge is normalized to 1 rad/s. * \(\Omega_{trans,LP_equiv} = \frac{1}{\min(\Omega_{p,LP1}, \Omega_{p,LP2})}\) * This is the effective transition ratio (stopband edge / passband edge) for the equivalent low-pass prototype.
3. Determine the Filter Order (N) & Denormalization
Calculated based on specs and chosen filter type. scipy.signal.buttord (and similar for other types) can find this for analog filters.
# --- Continued from previous Python block ---# Determine minimum order N and natural frequency Wn for Butterworth# analog=True is crucial here!N, Wn_buttord = signal.buttord(Wp_rads, Ws_rads, gpass_db, gstop_db, analog=True)print(f"\nRequired Butterworth Filter Order (N): {N}")# Wn_buttord is the natural frequency (cutoff) in rad/s# For Butterworth, this Wn is the -3dB cutoff frequencyprint(f"Butterworth Natural Frequency (Wn_buttord): {Wn_buttord:.2f} rad/s")
Wn_buttord is the natural angular frequency (\(\Omega_n\)) for the filter. For Butterworth, this is the -3dB cutoff frequency \(\Omega_c\).
3. Determine the Filter Order (N) & Denormalization
Normalized Lowpass to Denormalized Lowpass
Goal: Transform a normalized LP (\(\omega_c = 1\)) to a denormalized LP with cutoff \(\Omega_c\).
Transformation: Replace \(s_{norm}\) with \(s / \Omega_c\). \[s_{norm} \to \frac{s}{\Omega_c}\]
Denormalized Transfer Function:\[H_{LP}(s) = H_{norm}\left(\frac{s}{\Omega_c}\right)\]
Normalized Lowpass to Denormalized Highpass
Goal: Transform a normalized LP (\(\omega_c = 1\)) to a denormalized HP with cutoff \(\Omega_c\).
Transformation: Replace \(s_{norm}\) with \(\Omega_c / s\). \[s_{norm} \to \frac{\Omega_c}{s}\]
Denormalized Transfer Function:\[H_{HP}(s) = H_{norm}\left(\frac{\Omega_c}{s}\right)\]
Normalized Lowpass to Denormalized Bandpass
Goal: Transform a normalized LP (\(\omega_c = 1\)) to a denormalized BP with center frequency \(\Omega_0\) and bandwidth \(B\).
scipy.signal functions like butter, cheby1, etc., (with analog=True) perform these steps internally to give the final analog filter transfer function \(H_a(s)\).
\(H_a(s)\) can be represented as: 1. Numerator (\(b\)) and Denominator (\(a\)) coefficients: \(H_a(s) = \frac{b_0 s^M + b_1 s^{M-1} + \dots + b_M}{a_0 s^N + a_1 s^{N-1} + \dots + a_N}\) 2. Zeros (\(z\)), Poles (\(p\)), and Gain (\(k\)): \(H_a(s) = k \frac{\prod (s-z_i)}{\prod (s-p_j)}\)
Using signal.butter with analog=True. The Wn parameter here should be the cutoff frequency \(\Omega_c\). For buttord, the returned Wn_buttord is exactly this \(\Omega_c\).
# --- Continued from previous Python block ---# Design the ANALOG Butterworth filter# Use N and Wn_buttord from buttord# output='ba' gives numerator/denominator coefficientsb_analog, a_analog = signal.butter(N, Wn_buttord, btype='low', analog=True, output='ba')print("\nAnalog Filter Coefficients (Numerator b, Denominator a):")print(f"b_analog = {np.round(b_analog, 5)}")print(f"a_analog = {np.round(a_analog, 5)}")# Alternatively, get Zeros, Poles, Gain (ZPK)z_analog, p_analog, k_analog = signal.butter(N, Wn_buttord, btype='low', analog=True, output='zpk')print("\nAnalog Filter Zeros, Poles, Gain (ZPK):")print(f"Zeros (z_analog) = {z_analog}") # Will be empty for LP Butterworthprint(f"Poles (p_analog) = {p_analog}")print(f"Gain (k_analog) = {k_analog:.2f}")
Visualizing Analog Frequency Response (Python)
Use scipy.signal.freqs to compute the frequency response of an analog filter given its \(b,a\) coefficients.
# --- Continued from previous Python block ---# Frequencies for plotting (in rad/s)w_plot_rads = np.linspace(0.1, 2* Ws_rads, 2000) # Up to twice stopband edge# Calculate frequency response H(s) for s = j*ww_response, h_response = signal.freqs(b_analog, a_analog, worN=w_plot_rads)# Convert to Hz for plotting convenience if desiredw_plot_hz = w_response / (2* np.pi)plt.figure(figsize=(10, 6))plt.subplot(2, 1, 1)plt.plot(w_plot_hz, 20* np.log10(np.abs(h_response)))plt.title(f'Analog Butterworth LPF (Order N={N}) Frequency Response')plt.ylabel('Magnitude [dB]')plt.grid(True, which='both', axis='both')plt.axvline(fp_hz, color='green', linestyle='--', label=f'fp={fp_hz} Hz')plt.axvline(fs_hz, color='red', linestyle='--', label=f'fs={fs_hz} Hz')plt.axhline(-gpass_db, color='green', linestyle=':', label=f'-{gpass_db}dB')plt.axhline(-gstop_db, color='red', linestyle=':', label=f'-{gstop_db}dB')plt.legend()# Set x-axis to log scale for better visualization over wide frequency rangeplt.xscale('log')plt.xlim(fp_hz/10, fs_hz*5) # Adjust xlim as neededplt.ylim(-60, 5) # Adjust ylim as neededplt.subplot(2, 1, 2)plt.plot(w_plot_hz, np.angle(h_response, deg=True))plt.xlabel('Frequency [Hz]')plt.ylabel('Phase [degrees]')plt.grid(True, which='both', axis='both')plt.xscale('log')plt.xlim(fp_hz/10, fs_hz*5) # Adjust xlim as neededplt.tight_layout()plt.show()
6. Synthesize the Circuit (Realization)
Convert the mathematical \(H_a(s)\) into a physical electronic circuit. This step is not done by scipy.signal. It requires circuit design knowledge.
Passive Filters:
Resistors (R), Inductors (L), Capacitors (C).
Commonly “ladder” structures.
Inductors can be bulky/non-ideal, especially at low frequencies.
Common topologies: Sallen-Key, Multiple Feedback (MFB), State-Variable/Biquad.
Higher-order filters are often cascaded 2nd-order sections.
7. Component Selection and Simulation
Component Values: Choose standard, available component values (R, C, L, Op-Amps) that are close to calculated ideals.
Tolerances: Account for component variations.
Non-Idealities: Consider real-world behavior of components.
Circuit Simulation (Essential):
Use SPICE-based simulators (LTspice, QUCS, PSpice, etc.).
Verify performance against specifications.
Iterate on design and component values as needed.
This is also outside the scope of scipy.signal but critical for hardware implementation.
Tools for Analog Filter Design
Mathematical Software:
Python with scipy.signal: Excellent for determining filter parameters (\(N, \Omega_c\)) and transfer functions (\(b,a\) or \(z,p,k\)), and plotting responses.
MATLAB (Signal Processing Toolbox), Octave.
Filter Design Calculators/Software:
Texas Instruments FilterPro™, Analog Devices Filter Wizard®.
Online calculators and dedicated filter design suites.
Circuit Simulators:
LTspice, QUCS, PSpice, NI Multisim™, Keysight ADS.
Filter Design Handbooks:
Classic texts (e.g., by Van Valkenburg, Zverev) provide theory, tables, and formulas.
Conclusion – For Analog Filter Design
Analog filter design is a multi-step process:
Specs \(\rightarrow\) Theory \(\rightarrow\) Math (\(H_a(s)\)) \(\rightarrow\) Circuit \(\rightarrow\) Verification.
scipy.signal in Python is a powerful tool for the mathematical parts: determining order, calculating transfer function coefficients/poles/zeros, and analyzing frequency response for various analog filter prototypes.
Realizing the physical circuit requires additional circuit design knowledge and simulation tools.
Step 5: Apply Digital Transformation
Convert the designed analog filter \(H_a(s)\) to the digital domain \(H(z)\):
If Impulse Invariance: Use partial fractions and the \(s_k \rightarrow e^{s_k T}\) mapping.
If Bilinear Transformation: Substitute \(s = \frac{2}{T} \frac{1 - z^{-1}}{1 + z^{-1}}\) into \(H_a(s)\) and simplify algebraically.
Result: The digital filter transfer function: \[H(z) = \frac{\sum_{k=0}^{M} b_k z^{-k}}{1 + \sum_{k=1}^{N} a_k z^{-k}}\]
Step 6: Realization (Implementation)
The transfer function \(H(z)\) corresponds directly to a difference equation: \[y[n] = \sum_{k=0}^{M} b_k x[n-k] - \sum_{k=1}^{N} a_k y[n-k]\]
\(x[n]\): Input sequence
\(y[n]\): Output sequence
\(b_k, a_k\): Filter coefficients derived from \(H(z)\).
The feedback term (sum involving \(y[n-k]\)) makes the impulse response potentially infinite (IIR).
Summary: IIR Design Process
Define Digital Specs (\(\omega_p, \omega_s, A_p, A_s\)).
Note: Scipy often uses frequencies normalized to Nyquist:\(W_p = f_p / (F_s/2)\), \(W_s = f_s / (F_s/2)\)
Tolerances:
Max. passband ripple/attenuation: \(A_p\) or gpass (dB)
Min. stopband attenuation: \(A_s\) or gstop (dB)
Step 1: Example Specifications (Python)
import numpy as npfrom scipy import signalimport matplotlib.pyplot as plt# Filter Specificationsfs =10000# Sampling frequency (Hz)fp =1000# Passband edge frequency (Hz)fs_stop =1500# Stopband edge frequency (Hz)gpass =1# Passband ripple (dB) - max lossgstop =40# Stopband attenuation (dB) - min attenuation# Normalize frequencies to Nyquist frequency (fs/2)wp = fp / (fs /2)ws = fs_stop / (fs /2)print(f"Sampling Frequency: {fs} Hz")print(f"Normalized Passband Edge: {wp:.3f} (pi rad/sample)")print(f"Normalized Stopband Edge: {ws:.3f} (pi rad/sample)")print(f"Max Passband Loss: {gpass} dB")print(f"Min Stopband Attenuation: {gstop} dB")
Steps 2-4: Finding Order & Designing (Conceptual)
Transformation: Bilinear Transform is implicitly used by many scipy.signal functions for IIR design.
Frequency Pre-warping: Handled internally by Scipy when given digital frequency specs.
Analog Design: Scipy calculates the required analog prototype order and parameters based on the (internally pre-warped) specs.
Goal: Find filter order \(N\) and digital cutoff frequency \(W_n\).
Steps 2-4: Finding Order & Wn (Python - Butterworth Example)
Use scipy.signal.buttord to find the minimum order and cutoff frequency for a digital Butterworth filter meeting the specs.
# --- Continued from previous code block ---# Find the minimum order and cutoff frequencyN, Wn = signal.buttord(wp, ws, gpass, gstop, analog=False)print(f"\nMinimum Filter Order (N): {N}")# Wn is the digital cutoff freq (normalized) for butter functionprint(f"Butterworth Natural Frequency (Wn): {Wn:.3f} (pi rad/sample)")
analog=False specifies we want parameters for a digital filter.
Wn is the digital cutoff frequency (scalar for LP/HP, tuple for BP/BS) required by the butter function. It’s often close to wp.
Step 5: Designing the Digital Filter (Python - Butterworth)
Use scipy.signal.butter (or cheby1, cheby2, ellip) to get the filter coefficients \(b\) (numerator) and \(a\) (denominator) of \(H(z)\).
# --- Continued from previous code block ---# Design the digital Butterworth filter# btype='low' for lowpassb, a = signal.butter(N, Wn, btype='low', analog=False, output='ba')print("\nFilter Coefficients:")print(f"Numerator (b): {np.round(b, 5)}")print(f"Denominator (a): {np.round(a, 5)}")# H(z) = (b[0] + b[1]z^-1 + ...)/(a[0] + a[1]z^-1 + ...)# Note: a[0] is usually 1 after normalization